/**
  * @file		serial.c
  * @author		Naresuan University: Advance Computer System, class of 2013
  * @version	V1.0.0
  * @date		18-September-2013
  * @brief		Serial module
  */

/* Includes ------------------------------------------------------------------*/
#include "serial.h"

/* Private define ------------------------------------------------------------*/
#define MAX_UART_BUF_LENGTH		50

/* Private typedef -----------------------------------------------------------*/
typedef struct {
    uint32_t  Tx_HeadIdx;
    uint32_t  Tx_TailIdx;
    uint8_t   Tx[MAX_UART_BUF_LENGTH];
    uint32_t  Rx_HeadIdx;
    uint32_t  Rx_TailIdx;
    uint8_t   Rx[MAX_UART_BUF_LENGTH];	
} UART_RingBuffer;

/* Private macro -------------------------------------------------------------*/
#define __BUF_IS_FULL(headidx, tailidx)		((tailidx)==(((headidx)+1)%MAX_UART_BUF_LENGTH))
#define __BUF_IS_EMPTY(headidx, tailidx)	((headidx)==(tailidx))
#define __BUF_RESET(bufidx)					((bufidx)=0)
#define __BUF_INCR(bufidx)					((bufidx)=((bufidx)+1)%MAX_UART_BUF_LENGTH)
#define __BUF_ALLOC(headidx, tailidx)		((headidx>=tailidx)?(headidx-tailidx):(MAX_UART_BUF_LENGTH-tailidx+headidx))

/* Private variables ---------------------------------------------------------*/
static UART_RingBuffer UART_Buffer;
volatile static int UART_TxIrqState = RESET;

/* Private function prototypes -----------------------------------------------*/
static void USART_TxIRQHandler(void);
static void USART_RxIRQHandler(void);

/* Private functions ---------------------------------------------------------*/

// Transmit IRQ Handler: move one byte from buffer to USART_DR.
static void USART_TxIRQHandler()
{
	if (!__BUF_IS_EMPTY(UART_Buffer.Tx_HeadIdx, UART_Buffer.Tx_TailIdx)) {
		USART_SendData(USART1, UART_Buffer.Tx[UART_Buffer.Tx_TailIdx]);
		__BUF_INCR(UART_Buffer.Tx_TailIdx);
	} else {
		USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
		UART_TxIrqState = RESET;
	}
}

// Receive IRQ Handler: move incoming byte from USART_DR to buffer.
static void USART_RxIRQHandler()
{
	uint8_t val;
	
	if (!__BUF_IS_FULL(UART_Buffer.Rx_HeadIdx, UART_Buffer.Rx_TailIdx)) {
		UART_Buffer.Rx[UART_Buffer.Rx_HeadIdx] = USART_ReceiveData(USART1);  
		__BUF_INCR(UART_Buffer.Rx_HeadIdx);
	} else {
		val = USART_ReceiveData(USART1); // dummy read
	}
}


/**
  * @brief		IRQ handler for USART1: manage internal buffer for Tx/Rx operations
  * @param		None
  * @retval		None
  */
void USART1_IRQHandler()
{
	if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
		USART_RxIRQHandler();
	}
	if (USART_GetITStatus(USART1, USART_IT_TXE) != RESET) {
		USART_TxIRQHandler();
	}
}


/**
  * @brief		Sets the data rate in bits per second (baud) for serial data transmission.
  * @param		speed		Baud rate of USART, should be 9600
  * @retval		None
  * @warning	For PC communication, use one of these rates: 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, or 115200.
  * @remark		Default setting as 8 data bit, no parity bit, 1 stop bit
  */
void Serial_begin(uint32_t speed)
{
	GPIO_InitTypeDef gpio_init_struct;
	USART_InitTypeDef usart_init_struct;
	NVIC_InitTypeDef nvic_init_struct;
	
	// Turn on USART1 and corresponding GPIO
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);

	// Configure AFIO as Tx and Rx pins
	gpio_init_struct.GPIO_Mode = GPIO_Mode_AF_PP;
    gpio_init_struct.GPIO_Pin = GPIO_Pin_9;
    gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &gpio_init_struct);
    gpio_init_struct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    gpio_init_struct.GPIO_Pin = GPIO_Pin_10;
    GPIO_Init(GPIOA, &gpio_init_struct);

	// Configure USART1 (speed, 8, n, 1)
	usart_init_struct.USART_BaudRate = speed;
    usart_init_struct.USART_WordLength = USART_WordLength_8b;
    usart_init_struct.USART_StopBits = USART_StopBits_1;
    usart_init_struct.USART_Parity = USART_Parity_No;
    usart_init_struct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    usart_init_struct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART1, &usart_init_struct);

	// Configure and enable USART1 IRQ
	nvic_init_struct.NVIC_IRQChannel = USART1_IRQn;
    nvic_init_struct.NVIC_IRQChannelPreemptionPriority = 0;
    nvic_init_struct.NVIC_IRQChannelSubPriority = 0;
    nvic_init_struct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&nvic_init_struct);
	USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

	// Turn on USART1
	USART_Cmd(USART1, ENABLE);
	
	// Initialize UART buffer
	__BUF_RESET(UART_Buffer.Rx_HeadIdx);
	__BUF_RESET(UART_Buffer.Rx_TailIdx);
	__BUF_RESET(UART_Buffer.Tx_HeadIdx);
	__BUF_RESET(UART_Buffer.Tx_TailIdx);	
}


/**
  * @brief		Reads incoming serial data.
  * @param		None
  * @retval		First byte of incoming serial data.
  * @remark		-1 if no data is available.
  */
int Serial_read()
{
	int val = -1;

	while (__BUF_IS_EMPTY(UART_Buffer.Rx_HeadIdx, UART_Buffer.Rx_TailIdx));
	USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
	val = UART_Buffer.Rx[UART_Buffer.Rx_TailIdx];
	__BUF_INCR(UART_Buffer.Rx_TailIdx);
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

	return val;
}


/**
  * @brief		Writes binary data to the serial port.
  * @param		val			Byte to be written.
  * @retval		Number of bytes written.
  */
uint8_t Serial_write(uint8_t val)
{
	if (__BUF_IS_EMPTY(UART_Buffer.Tx_HeadIdx, UART_Buffer.Tx_TailIdx) && (UART_TxIrqState == RESET)) {
		USART_SendData(USART1, val);
		UART_TxIrqState = SET;
	} else {
		while (__BUF_IS_FULL(UART_Buffer.Tx_HeadIdx, UART_Buffer.Tx_TailIdx));
        USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
		UART_Buffer.Tx[UART_Buffer.Tx_HeadIdx] = val;
		__BUF_INCR(UART_Buffer.Tx_HeadIdx);			
	}
	USART_ITConfig(USART1, USART_IT_TXE, ENABLE);

	return 1;
}


/**
  * @brief		Waits for the transmission of outgoing serial data to complete.
  * @param		None
  * @retval		None
  */
void Serial_flush()
{
	while(!__BUF_IS_EMPTY(UART_Buffer.Tx_HeadIdx, UART_Buffer.Tx_TailIdx) || (UART_TxIrqState == SET));
}


/**
  * @brief		Get the number of bytes available for reading from the serial port.
  * @param		None
  * @retval		Number of bytes available to read.
  */
int Serial_available()
{
	return __BUF_ALLOC(UART_Buffer.Rx_HeadIdx, UART_Buffer.Rx_TailIdx);
}
